﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.ComponentModel;
using System.Windows.Media;
using System.Windows.Shapes;

namespace CandleStickChart.Stock
{
    public class ChartStyle
    {
        #region Constants
        const double DEFAULT_CANVAS_WIDTH = 300;
        const double DEFAULT_CANVAS_HEIGHT = 300;
        #endregion
        
        #region Fields & Properties
        private string title;
        private string xLabel;
        private string yLabel;
        private Canvas textCanvas;

        // used to Switch on and off GridLines
        private bool isXGrid = true;
        private bool isYGrid = true;
        private Brush gridlineColor = Brushes.LightGray;

        private double xTick = 1;
        private double yTick = 0.5;
        private GridlinePatternEnum gridlinePattern;
        private double leftOffset = 20;
        private double bottomOffset = 15;
        private double rightOffset = 10;
        private Line gridline = new Line();

        public string Title
        {
            get { return title; }
            set { title = value; }
        }
        public string XLabel
        {
            get { return xLabel; }
            set { xLabel = value; }
        }
        public string YLabel
        {
            get { return yLabel; }
            set { yLabel = value; }
        }
        public GridlinePatternEnum GridlinePattern
        {
            get { return gridlinePattern; }
            set { gridlinePattern = value; }
        }
        public double XTick
        {
            get { return xTick; }
            set { xTick = value; }
        }
        public double YTick
        {
            get { return yTick; }
            set { yTick = value; }
        }
        public Brush GridlineColor
        {
            get { return gridlineColor; }
            set { gridlineColor = value; }
        }
        public Canvas TextCanvas
        {
            get { return textCanvas; }
            set { textCanvas = value; }
        }
        public bool IsXGrid
        {
            get { return isXGrid; }
            set { isXGrid = value; }
        }
        public bool IsYGrid
        {
            get { return isYGrid; }
            set { isYGrid = value; }
        }
        private Canvas _chartCanvas;

        public Canvas ChartCanvas
        {
            get { return _chartCanvas; }
            set { _chartCanvas = value; }
        }

        private double _xmin;

        public double XMin
        {
            get { return _xmin; }
            set { _xmin = value; }
        }

        private double _xmax;

        public double XMax
        {
            get { return _xmax; }
            set { _xmax = value; }
        }

        private double _ymin;

        public double YMin
        {
            get { return _ymin; }
            set { _ymin = value; }
        }
        private double _ymax;

        public double YMax
        {
            get { return _ymax; }
            set { _ymax = value; }
        }
        #endregion

        public ChartStyle()
        {
            this.XMin = 0;
            this.XMax = 10;
            this.YMin = 0;
            this.YMax = 10;
        }
        public ChartStyle(double xmin, double xmax, double ymin, double ymax)
        {
            this.XMax = xmax;
            this.XMin = xmin;
            this.YMax = ymax;
            this.YMin = ymin;
        }
       
        public Point NormalizePoint(Point p)
        {
            if (double.IsNaN(ChartCanvas.Width))
                ChartCanvas.Width = DEFAULT_CANVAS_WIDTH;

            if(double.IsNaN(ChartCanvas.Height))
                ChartCanvas.Height = DEFAULT_CANVAS_HEIGHT;

            Point result = new Point();

            result.X = (p.X - XMin) * ChartCanvas.Width / (XMax - XMin);

            result.Y = ChartCanvas.Height - (p.Y - YMin) * ChartCanvas.Height / (YMax - YMin);

            return result;
        }

        public double TimeSpanToDouble(TimeSpan ts)
        {
            DateTime dt = DateTime.Parse("1 Jan");
            double d1 = BitConverter.ToDouble(BitConverter.GetBytes(dt.Ticks), 0);
            dt += ts;
            double d2 = BitConverter.ToDouble(BitConverter.GetBytes(dt.Ticks), 0);
            return d2 - d1;
        }
        public DateTime DoubleToDate(double d)
        {
            return new DateTime(BitConverter.ToInt64(BitConverter.GetBytes(d), 0));
        }
        public double DateToDouble(string date)
        {
            DateTime dt = DateTime.Parse(date);
            return BitConverter.ToDouble(BitConverter.GetBytes(dt.Ticks), 0);
        }

        public void AddChartStyle(TextBlock tbTitle, TextBlock tbXLabel,
                                 TextBlock tbYLabel, DataSeries ds)
        {
            Point pt = new Point();
            Line tick = new Line();
            double offset = 0;
            double dx, dy;
            TextBlock tb = new TextBlock();

            //  determine right offset: 
            tb.Text = XMax.ToString();
            tb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            Size size = tb.DesiredSize;
            rightOffset = size.Width / 2 + 2;

            // Determine left offset: 
            for (dy = YMin; dy <= YMax; dy += YTick)
            {
                pt = NormalizePoint(new Point(XMin, dy));
                tb = new TextBlock();
                tb.Text = dy.ToString();
                tb.TextAlignment = TextAlignment.Right;
                tb.Measure(new Size(Double.PositiveInfinity,
                                    Double.PositiveInfinity));
                size = tb.DesiredSize;
                if (offset < size.Width)
                    offset = size.Width;
            }
            leftOffset = offset + 5;
            Canvas.SetLeft(ChartCanvas, leftOffset);
            Canvas.SetBottom(ChartCanvas, bottomOffset);
            ChartCanvas.Width = Math.Abs(TextCanvas.Width -
                                leftOffset - rightOffset);
            ChartCanvas.Height = Math.Abs(TextCanvas.Height -
                                 bottomOffset - size.Height / 2);
            Rectangle chartRect = new Rectangle();
            chartRect.Stroke = Brushes.Black;
            chartRect.Width = ChartCanvas.Width;
            chartRect.Height = ChartCanvas.Height;
            ChartCanvas.Children.Add(chartRect);

            // Create vertical gridlines:
            if (IsYGrid == true)
            {
                for (dx = XMin + XTick; dx < XMax; dx += XTick)
                {
                    gridline = new Line();
                    AddLinePattern();
                    gridline.X1 = NormalizePoint(new Point(dx, YMin)).X;
                    gridline.Y1 = NormalizePoint(new Point(dx, YMin)).Y;
                    gridline.X2 = NormalizePoint(new Point(dx, YMax)).X;
                    gridline.Y2 = NormalizePoint(new Point(dx, YMax)).Y;
                    ChartCanvas.Children.Add(gridline);
                }
            }
            // Create horizontal gridlines: 
            if (IsXGrid == true)
            {
                for (dy = YMin + YTick; dy < YMax; dy += YTick)
                {
                    gridline = new Line();
                    AddLinePattern();
                    gridline.X1 = NormalizePoint(new Point(XMin, dy)).X;
                    gridline.Y1 = NormalizePoint(new Point(XMin, dy)).Y;
                    gridline.X2 = NormalizePoint(new Point(XMax, dy)).X;
                    gridline.Y2 = NormalizePoint(new Point(XMax, dy)).Y;
                    ChartCanvas.Children.Add(gridline);
                }
            }

            // Create x-axis tick marks: 
            for (dx = XMin; dx < XMax; dx += xTick)
            {
                pt = NormalizePoint(new Point(dx, YMin));
                tick = new Line();
                tick.Stroke = Brushes.Black;
                tick.X1 = pt.X;
                tick.Y1 = pt.Y;
                tick.X2 = pt.X;
                tick.Y2 = pt.Y - 5;
                ChartCanvas.Children.Add(tick);

                if (dx >= 0 && dx < ds.DataItems.Count)
                {
                    tb = new TextBlock();
                    double d0 = DateToDouble(ds.DataItems[0].Date.ToString());
                    double d1 = DateToDouble(ds.DataItems[1].Date.ToString());
                    double d = DateToDouble(ds.DataItems[(int)dx].Date.ToString());
                    if (d0 > d1)
                        d = DateToDouble(ds.DataItems[
                                ds.DataItems.Count - 1 - (int)dx].Date.ToString());
                    tb.Text = DoubleToDate(d).ToString("m");
                    tb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    size = tb.DesiredSize;
                    TextCanvas.Children.Add(tb);
                    Canvas.SetLeft(tb, leftOffset + pt.X - size.Width / 2);
                    Canvas.SetTop(tb, pt.Y + 2 + size.Height / 2);
                }
            }

            for (dy = YMin; dy <= YMax; dy += YTick)
            {
                pt = NormalizePoint(new Point(XMin, dy));
                tick = new Line();
                tick.Stroke = Brushes.Black;
                tick.X1 = pt.X;
                tick.Y1 = pt.Y;
                tick.X2 = pt.X + 5;
                tick.Y2 = pt.Y;
                ChartCanvas.Children.Add(tick);

                tb = new TextBlock();
                tb.Text = dy.ToString();

                tb.Measure(new Size(Double.PositiveInfinity,
                                    Double.PositiveInfinity));
                size = tb.DesiredSize;
                TextCanvas.Children.Add(tb);
                Canvas.SetRight(tb, ChartCanvas.Width + 10);
                Canvas.SetTop(tb, pt.Y);
            }

            // Add title and labels: 
            tbTitle.Text = Title;
            tbXLabel.Text = XLabel;
            tbYLabel.Text = YLabel;
            tbXLabel.Margin = new Thickness(leftOffset + 2, 2, 2, 2);
            tbTitle.Margin = new Thickness(leftOffset + 2, 2, 2, 2);
        }

        // Apply Line Pattern
        public void AddLinePattern()
        {
            gridline.Stroke = GridlineColor;
            gridline.StrokeThickness = 1;

            switch (GridlinePattern)
            {
                case GridlinePatternEnum.Dash:
                    gridline.StrokeDashArray =
                             new DoubleCollection(new double[2] { 4, 3 });
                    break;
                case GridlinePatternEnum.Dot:
                    gridline.StrokeDashArray =
                             new DoubleCollection(new double[2] { 1, 2 });
                    break;
                case GridlinePatternEnum.DashDot:
                    gridline.StrokeDashArray =
                             new DoubleCollection(new double[4] { 4, 2, 1, 2 });
                    break;
            }
        }
        public enum GridlinePatternEnum
        {
            Solid = 1,
            Dash = 2,
            Dot = 3,
            DashDot = 4
        }

    }
}
